/*******************************************************************************
 *                                    ZLG
 *                         ----------------------------
 *                         innovating embedded platform
 *
 * Copyright (c) 2001-present Guangzhou ZHIYUAN Electronics Co., Ltd.
 * All rights reserved.
 *
 * Contact information:
 * web site:    https://www.zlg.cn
 *******************************************************************************/
/*******************************************************************************
 * Includes
 ******************************************************************************/
#include "driver/uart/hpm6e00_uart.h"

/*******************************************************************************
 * Code
 ******************************************************************************/
/**
 * \brief 串口寄存器检查
 */
static int __uart_reg_chk(hpm_uart_reg_t *p_uart_reg){
    if (p_uart_reg == NULL) {
        return -EINVAL;
    }
    if ((p_uart_reg != HPM_UART0) &&
            (p_uart_reg != HPM_UART1) &&
            (p_uart_reg != HPM_UART2) &&
            (p_uart_reg != HPM_UART3) &&
            (p_uart_reg != HPM_UART4) &&
            (p_uart_reg != HPM_UART5) &&
            (p_uart_reg != HPM_UART6) &&
            (p_uart_reg != HPM_UART7) &&
            (p_uart_reg != HPM_UART8) &&
            (p_uart_reg != HPM_UART9) &&
            (p_uart_reg != HPM_UART10) &&
            (p_uart_reg != HPM_UART11) &&
            (p_uart_reg != HPM_UART12) &&
            (p_uart_reg != HPM_UART13) &&
            (p_uart_reg != HPM_UART14) &&
            (p_uart_reg != HPM_UART15)) {
        return -EINVAL;
    }
    return 0;
}

/**
 * \brief 波特率计算
 *
 * \param[in] freq      串口时钟频率
 * \param[in] baudrate  要设置的波特率
 * \param[in] p_div_out 存储返回的分频的缓存
 * \param[in] p_osc_out 存储返回的过采样率的缓存
 *
 * \retval 成功返回 0
 */
static int __uart_baudrate_calculate(uint32_t  freq,
                                     uint32_t  baudrate,
                                     uint16_t *p_div_out,
                                     uint8_t  *p_osc_out){
    uint16_t div, osc, delta;
    float    tmp;

    if ((p_div_out == NULL) ||
            (p_osc_out == NULL) ||
            (freq == 0) ||
            (baudrate == 0) ||
            (baudrate < HPM_UART_MINIMUM_BAUDRATE) ||
            (freq / HPM_UART_BAUDRATE_DIV_MIN < baudrate * HPM_UART_OSC_MIN) ||
            (freq / HPM_UART_BAUDRATE_DIV_MAX > (baudrate * HPM_UART_OSC_MAX))) {
        return -EINVAL;
    }

    tmp = (float) freq / baudrate;

    for (osc = HPM_UART_OSC_MIN; osc <= HPM_UART_OSC_MAX; osc += 2) {
        /* osc range: 0 - 32, even number */
        delta = 0;
        div = (uint16_t)(tmp / osc);
        if (div < HPM_UART_BAUDRATE_DIV_MIN) {
            /* 无效分频*/
            continue;
        }
        if (div * osc > tmp) {
            delta = (uint16_t)(div * osc - tmp);
        } else if (div * osc < tmp) {
            delta = (uint16_t)(tmp - div * osc);
        }
        if (delta && ((delta * 100 / tmp) > HPM_UART_BAUDRATE_TOLERANCE)) {
            continue;
        } else {
            *p_div_out = div;
            *p_osc_out = (osc == HPM_UART_OSC_MAX) ? 0 : osc; /* osc == 0 in bitfield, oversample rate is 32 */
            return 0;
        }
    }
    return -EPERM;
}

/**
 * \brief 获取串口中断 ID
 *
 * \param[in] p_uart_reg 相关串口寄存器
 *
 * \retval 成功返回串口中断 ID
 */
uint32_t uart_irq_id_get(hpm_uart_reg_t *p_uart_reg){
    return (READ_REG32(p_uart_reg->IIR) & UART_IIR_INTRID_MASK);
}

/**
 * \brief 串口中断设置
 *
 * \param[in] p_uart_reg 相关串口寄存器
 * \param[in] mask       中断屏蔽位
 * \param[in] is_enable  是否使能
 *
 * \retval 成功返回 0
 */
int uart_int_set(hpm_uart_reg_t *p_uart_reg, uint32_t mask, uint8_t is_enable){
    int ret = __uart_reg_chk(p_uart_reg);
    if (ret != 0) {
        return -EINVAL;
    }

    if (is_enable) {
        SET_REG32_BIT(p_uart_reg->IER, mask);
    } else {
        CLEAR_REG32_BIT(p_uart_reg->IER, mask);
    }

    return 0;
}

/**
 * \brief 串口 Modem 配置
 */
static void __uart_modem_cfg(hpm_uart_reg_t *p_uart_reg, struct uart_modem_cfg *p_cfg){
    WRITE_REG32(p_uart_reg->MCR, UART_MCR_AFE_SET(p_cfg->auto_flow_ctrl_en) |
                                 UART_MCR_LOOP_SET(p_cfg->loop_back_en) |
                                 UART_MCR_RTS_SET(p_cfg->set_rts_high));
}

/**
 * \brief 串口接收空闲检测初始化
 *
 * \param[in] p_uart_reg   相关串口寄存器
 * \param[in] p_rxidle_cfg 接收空闲配置结构体
 *
 * \retval 成功返回 0
 */
int uart_init_rxline_idle_detection(hpm_uart_reg_t              *p_uart_reg,
                                    struct uart_rxline_idle_cfg *p_rxidle_cfg){
    p_uart_reg->IDLE_CFG &= ~(UART_IDLE_CFG_RX_IDLE_EN_MASK
                    | UART_IDLE_CFG_RX_IDLE_THR_MASK
                    | UART_IDLE_CFG_RX_IDLE_COND_MASK);
    p_uart_reg->IDLE_CFG |= UART_IDLE_CFG_RX_IDLE_EN_SET(p_rxidle_cfg->detect_enable)
                    | UART_IDLE_CFG_RX_IDLE_THR_SET(p_rxidle_cfg->threshold)
                    | UART_IDLE_CFG_RX_IDLE_COND_SET(p_rxidle_cfg->idle_cond);

    if (p_rxidle_cfg->detect_irq_enable) {
        uart_int_set(p_uart_reg, UART_IER_ERXIDLE_MASK, 1);
    } else {
        uart_int_set(p_uart_reg, UART_IER_ERXIDLE_MASK, 0);
    }

    return 0;
}

/**
 * \brief 串口默认配置设置
 *
 * \param[in] p_cfg 配置结构体
 */
void uart_cfg_default_set(struct hpm_uart_cfg *p_cfg){
    if (p_cfg == NULL) {
        return;
    }

    p_cfg->baudrate                     = 115200;
    p_cfg->word_length                  = word_length_8_bits;
    p_cfg->parity                       = parity_none;
    p_cfg->num_of_stop_bits             = stop_bits_1;
    p_cfg->fifo_enable                  = TRUE;
    p_cfg->rx_fifo_level                = uart_rx_fifo_trg_not_empty;
    p_cfg->tx_fifo_level                = uart_tx_fifo_trg_not_full;
    p_cfg->dma_enable                   = FALSE;
    p_cfg->modem_cfg.auto_flow_ctrl_en  = FALSE;
    p_cfg->modem_cfg.loop_back_en       = FALSE;
    p_cfg->modem_cfg.set_rts_high       = FALSE;
    p_cfg->rxidle_cfg.detect_enable     = FALSE;
    p_cfg->rxidle_cfg.detect_irq_enable = FALSE;
    p_cfg->rxidle_cfg.idle_cond         = uart_rxline_idle_cond_rxline_logic_one;
    p_cfg->rxidle_cfg.threshold         = 10; /* 10-bit for typical UART configuration (8-N-1) */
}

/**
 * \brief 串口初始化
 *
 * \param[in] p_uart_reg 相关串口寄存器
 * \param[in] p_cfg      配置结构体
 *
 * \retval 成功返回 0
 */
int uart_init(hpm_uart_reg_t *p_uart_reg, struct hpm_uart_cfg *p_cfg){
    int      ret;
    uint32_t tmp;
    uint8_t  osc;
    uint16_t div;

    ret = __uart_reg_chk(p_uart_reg);
    if ((ret != 0) || (p_cfg == NULL)) {
        return -EINVAL;
    }

    /* 关闭所有中断*/
    WRITE_REG32(p_uart_reg->IER, 0);
    /* 设置 DLAB 为 1 */
    SET_REG32_BIT(p_uart_reg->LCR, UART_LCR_DLAB_MASK);

    /* 计算波特率*/
    ret = __uart_baudrate_calculate(p_cfg->src_freq_in_hz, p_cfg->baudrate, &div, &osc);
    if (ret != 0) {
        return ret;
    }
    MODIFY_REG32(p_uart_reg->OSCR, UART_OSCR_OSC_MASK, UART_OSCR_OSC_SET(osc));

    WRITE_REG32(p_uart_reg->DLL, UART_DLL_DLL_SET(div >> 0));
    WRITE_REG32(p_uart_reg->DLM, UART_DLM_DLM_SET(div >> 8));

    /* 设置 DLAB 为 0 */
    CLEAR_REG32_BIT(p_uart_reg->LCR, UART_LCR_DLAB_MASK);

    tmp = READ_REG32(p_uart_reg->LCR);
    tmp &= ~(UART_LCR_SPS_MASK | UART_LCR_EPS_MASK | UART_LCR_PEN_MASK);

    switch (p_cfg->parity) {
        case parity_none:
            break;
        case parity_odd:
            tmp |= UART_LCR_PEN_MASK;
            break;
        case parity_even:
            tmp |= UART_LCR_PEN_MASK | UART_LCR_EPS_MASK;
            break;
        case parity_always_1:
            tmp |= UART_LCR_PEN_MASK | UART_LCR_SPS_MASK;
            break;
        case parity_always_0:
            tmp |= UART_LCR_EPS_MASK | UART_LCR_PEN_MASK
                | UART_LCR_SPS_MASK;
            break;
        default:
            return -EINVAL;
    }

    tmp &= ~(UART_LCR_STB_MASK | UART_LCR_WLS_MASK);
    switch (p_cfg->num_of_stop_bits) {
        case stop_bits_1:
            break;
        case stop_bits_1_5:
            tmp |= UART_LCR_STB_MASK;
            break;
        case stop_bits_2:
            if (p_cfg->word_length < word_length_6_bits) {
                return -EINVAL;
            }
            tmp |= UART_LCR_STB_MASK;
            break;
        default:
            return -EINVAL;
    }

    WRITE_REG32(p_uart_reg->LCR, tmp | (p_cfg->word_length));

    /* 复位发送和接收 FIFO */
    WRITE_REG32(p_uart_reg->FCR, UART_FCR_TFIFORST_MASK | UART_FCR_RFIFORST_MASK);
    /* 使能 FIFO */
    tmp = (p_cfg->fifo_enable << UART_FCR_FIFOE_POS)
        | (p_cfg->tx_fifo_level << UART_FCR_TFIFOT_POS)
        | (p_cfg->rx_fifo_level << UART_FCR_RFIFOT_POS)
        | (p_cfg->dma_enable << UART_FCR_DMAE_POS);

    WRITE_REG32(p_uart_reg->FCR, tmp);
    /* 存储 FCR 寄存器的值 */
    WRITE_REG32(p_uart_reg->GPR, tmp);

    __uart_modem_cfg(p_uart_reg, &p_cfg->modem_cfg);

    uart_init_rxline_idle_detection(p_uart_reg, &p_cfg->rxidle_cfg);

    SET_REG32_BIT(p_uart_reg->IDLE_CFG, UART_IDLE_CFG_RXEN_MASK);

    return 0;
}

/**
 * \brief 串口反初始化
 *
 * \param[in] p_uart_reg 相关串口寄存器
 */
void uart_deinit(hpm_uart_reg_t *p_uart_reg){
    int ret = __uart_reg_chk(p_uart_reg);
    if (ret != 0) {
        return;
    }
    /* 关闭所有中断*/
    WRITE_REG32(p_uart_reg->IER, 0);
    WRITE_REG32(p_uart_reg->LCR, 0);
    WRITE_REG32(p_uart_reg->OSCR, 16);
    WRITE_REG32(p_uart_reg->DLL, 16);
    WRITE_REG32(p_uart_reg->THR, 16);
    WRITE_REG32(p_uart_reg->DLM, 0);
    WRITE_REG32(p_uart_reg->FCR, UART_FCR_RFIFORST_MASK);
    WRITE_REG32(p_uart_reg->MCR, 0);
    WRITE_REG32(p_uart_reg->CFG, 0);
}


/**
 * \brief 串口字节发送
 *
 * \param[in] p_uart_reg 相关串口寄存器
 * \param[in] c          要发送的字节
 *
 * \retval 成功返回 1
 */
int uart_byte_send(hpm_uart_reg_t *p_uart_reg, uint8_t c){
    uint32_t retry = 0;
    int      ret;

    ret = __uart_reg_chk(p_uart_reg);
    if (ret != 0) {
        return -EINVAL;
    }

    while (!(READ_REG32(p_uart_reg->LSR) & UART_LSR_THRE_MASK)) {
        if (retry > HPM_UART_DRV_RETRY_COUNT) {
            break;
        }
        retry++;
    }

    if (retry > HPM_UART_DRV_RETRY_COUNT) {
        return -ETIME;
    }

    WRITE_REG32(p_uart_reg->THR, UART_THR_THR_SET(c));

    return 1;
}

/**
 * \brief 串口字节接收
 *
 * \param[in] p_uart_reg 相关串口寄存器
 * \param[in] p_c        字节缓存
 *
 * \retval 成功返回 1
 */
int uart_byte_receive(hpm_uart_reg_t *p_uart_reg, uint8_t *p_c){
    uint32_t retry = 0;

    while (!(READ_REG32(p_uart_reg->LSR) & UART_LSR_DR_MASK)) {
        if (retry > HPM_UART_DRV_RETRY_COUNT) {
            break;
        }
        retry++;
    }

    if (retry > HPM_UART_DRV_RETRY_COUNT) {
        return -ETIME;
    }

    *p_c = READ_REG32(p_uart_reg->RBR) & UART_RBR_RBR_MASK;

    return 1;
}



